package com.lyw.i12ThreadPool;

import java.util.concurrent.*;

/**
 * 1、线程池（thread pool）：
 *    一种线程使用模式。线程过多会带来调度开销，进而影响缓存局部性和整体性能。而线程池维护着多个线程，
 *    对线程统一管理。
 * 2、线程池就是存放线程的池子，池子里存放了很多可以复用的线程。
 * 3、使用线程池的优势
 *    - 降低资源消耗，提高效率，创建好一定数量的线程放在池中，等需要使用的时候就从池中拿一个，这要比需要的时候创建一个线
 *    程对象要快的多。
 *    - 减少了创建和销毁线程的次数，每个工作线程都可以被重复利用，可执行多个任务。
 *    - 提升系统响应速度，假如创建线程用的时间为T1，执行任务用的时间为T2,销毁线程用的时间为T3，那么使用线
 *    程池就免去了T1和T3的时间
 *
 * 4、四种创建线程池的方式
 *    Executors类（并发包）提供了4种创建线程池方法，这些方法最终都是通过配置ThreadPoolExecutor的不同参数
 *    ，来达到不同的线程管理效果。
 *
 *    - newCacheTreadPool 创建一个可以缓存的线程池，如果线程池长度超过处理需要，可以灵活回收空闲线程，没
 *    回收的话就新建线程
 *    总结: 线程池的最大核心线程为无限大，当执行第二个任务时第一个任务已经完成，则会复用执行第一个任务的线程；
 *    如果第一个线程任务还没有完成则会新建一个线程。
 *
 *    - newFixedThread 创建一个定长的线程池，可控制最大并发数，超出的线程进行队列等待。
 *    总结：创建指定长度的线程池，任务超出当前线程池执行线程数量则会一直等待，直到运行。
 *
 *    - newScheduleThreadPool 可以创建定长的、支持定时任务，周期任务执行。
 *
 *    - newSingleExecutor 创建一个单线程池，它只会用唯一的工作线程来执行任务，保证所有任务按照指定顺序
 *    (FIFO, LIFO, 优先级)执行。
 *
 * 5、推荐通过 new ThreadPoolExecutor() 的写法创建线程池，这样写线程数量更灵活，开发中多数用这个类创建线
 * 程。
 *    - corePoolSize 线程池中的核心线程数 核心线程生命周期无限，即使空闲也不会死亡。
 *    - maximumPoolSize 线程池中最大线程数 任务队列满了以后当有新任务进来则会增加一个线程来处理新任务，
 *    （线程总数 < maximumPoolSize）
 *    - keepAliveTime 闲置超时时间 当线程数大于核心线程数时，经过keepAliveTime时间将会回收非核心线程
 *    - unit 超时时间的单位（时/分/秒等） *
 *    - workQueue 线程池中的任务队列 存放任务(Runnable)的容器
 *    - threadFactory 为线程池提供创建新线程的线程工厂 *
 *    - rejectedExecutionHandler 拒绝策略 新增一个任务到线程池，如果线程池任务队列超过最大值之后,并且
 *    已经开启到最大线程数时，默认为抛出ERROR异常
 *
 * 6、多线程四种拒绝策略
 *    - AbortPolicy abortPolicy = new ThreadPoolExecutor.AbortPolicy();
 *      AbortPolicy 当任务添加到线程池中被拒绝时，直接丢弃任务，并抛出
 *      RejectedExecutionException异常。
 *
 *    - DiscardPolicy discardPolicy = new ThreadPoolExecutor.DiscardPolicy();
 *      当任务添加到线程池中被拒绝时，丢弃被拒绝的任务，不抛异常。
 *
 *    - DiscardOldestPolicy discardOldestPolicy = new ThreadPoolExecutor
 *      .DiscardOldestPolicy();
 *      当任务添加到线程池中被拒绝时，丢弃任务队列中最久的未处理任务，然后将被拒绝的任务添加到等待队列中。
 *
 *    - CallerRunsPolicy callerRunsPolicy = new ThreadPoolExecutor.CallerRunsPolicy();
 *      被拒绝任务的处理程序，直接在execute方法的调用线程中运行被拒绝的任务。
 *      总结：就是被拒绝的任务，直接在主线程中运行，不再进入线程池。
 *
 * 7、使用线程池时通常我们可以将执行的任务分为两类：
 *    - cpu 密集型任务
 *      cpu 密集型任务，需要线程长时间进行的复杂的运算，这种类型的任务需要少创建线程（以CPU核数+1为准），
 *      过多的线程将会频繁引起上文切换，降低任务处理速度。
 *    - io 密集型任务
 *      而 io 密集型任务，由于线程并不是一直在运行，可能大部分时间在等待 IO 读取/写入数据，增加线程数量可
 *      以提高并发度，尽可能多处理任务。
 *  最后给大家补充一个知识点，配置线程池最好的方式是可以动态修改线程池配置， 例如调用线程池的
 *  threadPoolExecutor.setCorePoolSize();方法， 搭配分布式配置中心可以随着运行场景动态的修改核心线程
 *  数等功能。
 *
 * 8、线程池执行原理
 *    - 首先通过线程池提交了一个线程任务
 *    - 线程池判断当前线程数是否小于核心线程数，Y 创建一个新的线程(核心线程数)执行任务 -》 结束
 *    - N 不满足上面的话，任务队列是否还可以添加任务，Y 将任务放入任务队列等待执行任务 -》 结束
 *    - N 不满足上面条件的话，判断线程池中的线程数是否小于最大线程数，Y 创建一个线程并执行 -》 结束
 *    - N 以上都不满足的话，执行拒绝策略
 *    多线程四种拒绝策略
 *      - AbortPolicy abortPolicy = new ThreadPoolExecutor.AbortPolicy();
 *        AbortPolicy 当任务添加到线程池中被拒绝时，直接丢弃任务，并抛出
 *        RejectedExecutionException异常。
 *      - DiscardPolicy discardPolicy = new ThreadPoolExecutor.DiscardPolicy();
 *        当任务添加到线程池中被拒绝时，丢弃被拒绝的任务，不抛异常。
 *      - DiscardOldestPolicy discardOldestPolicy = new ThreadPoolExecutor
 *        .DiscardOldestPolicy();
 *        当任务添加到线程池中被拒绝时，丢弃任务队列中最久的未处理任务，然后将被拒绝的任务添加到等待队列中。
 *      - CallerRunsPolicy callerRunsPolicy = new ThreadPoolExecutor.CallerRunsPolicy();
 *        被拒绝任务的处理程序，直接在execute方法的调用线程中运行被拒绝的任务。
 *        总结：就是被拒绝的任务，直接在主线程中运行，不再进入线程池。
 *
 *
 * 9、现实中上面创建线程池的方式都不会用
 *    他们的请求队列长度个别都是 Integer.MAX_VALUE，可能会堆积大量的请求，从而导致 OOM
 * @author lyw
 * @Date 2023/7/11-下午4:32
 */
public class ThreadPoolDemo {

  public static void main(String[] args) {

    // 一池多线程
    /*ExecutorService threadPool1 = Executors.newFixedThreadPool(3);
    try {
      for (int i = 1; i <= 5; i++) {
        threadPool1.execute(() -> {
          System.out.println(Thread.currentThread().getName() + " 办理了业务！");
        });
      }
    } finally {
      threadPool1.shutdown();
    }*/

    // 定长线程超过最大数据排队等待
    /*ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
    try {
      for (int i = 1; i <= 10; i++) {
        threadPool2.execute(() -> {
          System.out.println(Thread.currentThread().getName() + " 完成了业务！");
        });
      }
    } finally {
      threadPool2.shutdown();
    }*/

    // 能够完成周期性任务
    /*ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(5);
    try {
      for (int i = 1; i <= 5; i++) {
        ScheduledFuture<?> scheduledFuture = scheduled.scheduleAtFixedRate(() -> {
          System.out.println(Thread.currentThread().getName() + " 123");
        }, 0, 10, TimeUnit.SECONDS);
      }
    } finally {
      scheduled.shutdown();
    }*/

    /*// 可扩容的线程池
    ExecutorService threadPool = Executors.newCachedThreadPool();
    try {
      for (int i = 1; i <= 10; i++) {
        threadPool.execute(() -> {
          System.out.println(Thread.currentThread().getName() + " 完成了业务!");
        });
      }
    } finally {
      threadPool.shutdown();
    }*/

    // 自己创建一个线程池使用
    ThreadPoolExecutor threadPoolExecutor =
            new ThreadPoolExecutor(2,
                    5,
                    2,
                    TimeUnit.MINUTES,
                    new ArrayBlockingQueue<>(3),
                    Executors.defaultThreadFactory(),
                    new ThreadPoolExecutor.CallerRunsPolicy());

    try {
      for (int i = 1; i <= 11; i++) {
        threadPoolExecutor.execute(() -> {
          System.out.println(Thread.currentThread().getName() + " 完成了业务!");
        });
      }
    } finally {
      threadPoolExecutor.shutdown();
    }

  }
}
